package com.binance.gateway.app.base.helper;

import com.binance.gateway.app.base.dto.SessionDto;
import com.binance.gateway.app.base.vo.CommonRet;
import com.binance.gateway.app.constant.Constant;
import com.binance.gateway.app.constant.TokenType;
import com.binance.gateway.util.DomainUtil;
import com.binance.master.enums.LanguageEnum;
import com.binance.master.enums.TerminalEnum;
import com.binance.master.error.GeneralCode;
import com.binance.master.models.APIRequest;
import com.binance.master.models.APIResponse;
import com.binance.master.models.APIResponse.Status;
import com.binance.master.utils.Md5Tools;
import com.binance.master.utils.WebUtils;
import com.binance.master.web.handlers.MessageHelper;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@Component
public class BaseHelper {
    private static final int CSRF_SHOW_LEN = 8;
    @Resource
    private MessageHelper messageHelper;
    @Resource
    private SysConfigHelper sysConfigHelper;


    public CommonRet<Void> getCommonRet(GeneralCode generalCode, HttpServletRequest request) {
        CommonRet<Void> ret = new CommonRet<>();
        ret.setCode(generalCode.getCode());
        ret.setMessage(messageHelper.getMessage(generalCode));
        return ret;
    }



    public String getLanguage() {
        HttpServletRequest request = WebUtils.getHttpServletRequest();
        if (request != null) {
            return getLastLoginLanguage(request);
        }
        return "en";
    }

    /**
     * 获取语言版本，如果获取失败则根据用户最后登录地址判断语言版本
     *
     * @param request
     * @return String
     */
    private String getLastLoginLanguage(final HttpServletRequest request) {
        String language = request.getParameter(Constant.LANG);
        if (StringUtils.isBlank(language)) {
            language = request.getHeader(Constant.LANG);
        }
        if (StringUtils.isBlank(language)) {
            language = getCookieValue(request, Constant.LANG);
        }
        if (StringUtils.isBlank(language) || StringUtils.equals(language, "undefined")) {
            language = "en";
        }
        return language;
    }

    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        Cookie cookie = org.springframework.web.util.WebUtils.getCookie(request, cookieName);
        if (cookie != null) {
            return cookie.getValue();
        }
        return null;
    }

    public static String getTokenFromCookie(HttpServletRequest request) {
        return getCookieValue(request, Constant.COOKIE_TOKEN);
    }

    public static String getFromHeadOrParam(String name) {
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        String value = request.getHeader(name);
        if (value == null) {
            value = request.getParameter(name);
        }
        return value;
    }

    public static void delCookie(HttpServletRequest request, HttpServletResponse response, Cookie c) {
        c = new Cookie(c.getName(), null);
        c.setDomain(DomainUtil.getTopDomain(request));
        c.setPath("/");
        c.setMaxAge(0);
        response.addCookie(c);
    }

    public static void delDomainCookie(HttpServletResponse response, Cookie c) {
        c = new Cookie(c.getName(), null);
        c.setPath("/");
        c.setMaxAge(0);
        response.addCookie(c);
    }

    public <T> APIRequest<T> getInstance(T body) {
        APIRequest<T> request = new APIRequest<T>();
        request.setLanguage(LanguageEnum.findByLang(getLanguage()));
        request.setTerminal(getTerminal());
        request.setBody(body);
        return request;
    }


    private TerminalEnum getTerminal() {
        String clientType = null;
        HttpServletRequest request = WebUtils.getHttpServletRequest();
        if (request != null) {
            clientType = request.getParameter("clientType");
            if (StringUtils.isBlank(clientType)) {
                clientType = request.getHeader("clientType");
            }
        }
        if (StringUtils.isBlank(clientType)) {
            clientType = "web";
        }
        return TerminalEnum.findByCode(clientType.toLowerCase());
    }

    public void deleteAllCookiesWhileLogout() {
        HttpServletRequest request = WebUtils.getHttpServletRequest();
        HttpServletResponse response = getResponse();
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                if (Constant.CLEAR_COOKIE_LIST.contains(c.getName())) {
                    BaseHelper.delCookie(request, response, c);
                }
                if (Constant.CLEAR_OLD_COOKIE_LIST.contains(c.getName())) {
                    BaseHelper.delDomainCookie(response, c);
                }
            }
        }
    }

    public HttpServletResponse getResponse() {
        HttpServletResponse response =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        return response;
    }


    public boolean isOk(APIResponse<?> resp) {
        if (resp.getStatus() == Status.OK) {
            return true;
        }
        return false;
    }

    public String getClientType(HttpServletRequest request) {
        String clientType = request.getParameter("clientType");
        if (StringUtils.isBlank(clientType)) {
            clientType = request.getHeader("clientType");
        }
        if (StringUtils.isNotBlank(clientType) && !"H5".equals(clientType) && !"WX".equals(clientType)) {
            clientType = clientType.toLowerCase();
        }
        return clientType;
    }

    /**
     * 检查token跟clientType是否匹配，考虑到前端部分url有可能不传clientType，暂时只打日志，
     * 后续考虑直接认为非登录态
     *
     * @param token
     */
    public boolean checkTokenType(String token) {
        boolean ret = false;
        HttpServletRequest request = WebUtils.getHttpServletRequest();
        String clientType = request.getHeader("clientType");
        if (StringUtils.isBlank(clientType)) {
            clientType = request.getParameter("clientType");
        }
        if (StringUtils.isBlank(clientType)) {
            log.warn("clientType is blank,url={}", request.getRequestURI());
        } else {
            TokenType tokenType = TokenType.getTokenType(clientType);
            // 若token为null，不会进入本方法，调用方已做判断
            if (token != null && !token.startsWith(tokenType.name())) {
                log.warn("token={} and clientType={} is not match,url={}", StringUtils.left(token, Constant.TOKEN_SHOW_LEN), clientType, request.getRequestURI());
            } else {
                // clientType跟token匹配成功才认定合法
                ret = true;
            }
        }
        return ret;
    }

    public boolean isFromWeb() {
        String clientType = BaseHelper.getFromHeadOrParam(Constant.HEADER_CLIENT_TYPE);
        return TerminalEnum.WEB.getCode().equalsIgnoreCase(clientType)
                || TerminalEnum.H5.getCode().equalsIgnoreCase(clientType);
    }

    /**
     * 获取pnk token，非中台token 若是web端从cookie获取，其他终端从header中获取
     * 
     * @return
     */
    public String getPnkToken(HttpServletRequest request) {
        String token = request.getParameter("token");
        if (StringUtils.isBlank(token)) {
            token = getTokenFromCookie(request);
        }
        return token;
    }

    public static String getTokenSegment(HttpServletRequest request) {
        // 默认web端，从cookie中获取
        String token = getTokenFromCookie(request);
        if (StringUtils.isBlank(token)) {
            // 若非web端，旧app是header或parameter查询
            token = getFromHeadOrParam("token");
            if (StringUtils.isBlank(token)) {
                // 新的app统一从header中拿
                token = request.getHeader(Constant.HEADER_TOKEN);
            }
        }
        return StringUtils.substring(token, 0, Constant.TOKEN_SHOW_LEN);
    }

    /**
     * 从token中解析userId，仅可用做打印日志，因为token可能是伪造的，不会去服务端做真实验证，直接从token解析
     *
     * @return
     */
    public static String transformUserIdFromToken(String token) {
        if (StringUtils.isNotBlank(token)) {
            return StringUtils.substringBetween(token, ".");
        }
        return "";
    }

    public boolean isValidCsrfToken(HttpServletRequest request, SessionDto sessionVo, String cookieName) {
        // if (HttpMethod.GET.toString().equals(request.getMethod())) {
        // // GET方法无需验证csrfToken
        // return true;
        // }
        String csrfToken = request.getHeader(cookieName);
        if (StringUtils.isEmpty(csrfToken)) {
            csrfToken = request.getParameter(cookieName);
        }
        if (csrfToken != null && StringUtils.isNotEmpty(sessionVo.getCsrfToken())
                && csrfToken.equalsIgnoreCase(Md5Tools.MD5(sessionVo.getCsrfToken()))) {
            return true;
        }
        // 监控闪退用户，1分钟内就退出的认为用户在闪退
        LocalDateTime loginTime = LocalDateTime.parse(sessionVo.getLoginTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        if (loginTime.isAfter(LocalDateTime.now().minusMinutes(1))) {
            log.warn("userId={} loginTime = {} csrfToken={} is login and logout in short time", sessionVo.getUserId(), sessionVo.getLoginTime(), StringUtils.left(csrfToken, CSRF_SHOW_LEN));
        }
        log.info("token={}, {} expected: {}, given: {},origin cookie={}",
                StringUtils.left(sessionVo.getToken(), Constant.TOKEN_SHOW_LEN), cookieName,
                StringUtils.left(Md5Tools.MD5(sessionVo.getCsrfToken()), CSRF_SHOW_LEN),
                StringUtils.left(csrfToken, CSRF_SHOW_LEN),
                StringUtils.left(BaseHelper.getCookieValue(request, cookieName), CSRF_SHOW_LEN));
        return false;
    }

    public String getBasePath(HttpServletRequest request) {
        try {
            StringBuffer requestURL = request.getRequestURL();
            String tempContextUrl =
                    requestURL.delete(requestURL.length() - request.getRequestURI().length(), requestURL.length())
                            .append("/").toString();

            URL url = new URL(tempContextUrl);
            String exch_domain = url.getProtocol() + "://" + url.getHost();

            String exchDomain = sysConfigHelper.getCodeByDisplayName("email_domain");
            String[] exchDomains = exchDomain.split(";");
            boolean flag = false;
            for (String domain : exchDomains) {
                if (domain.equals(exch_domain)) {
                    flag = true;
                    break;
                }
            }

            if (flag) {
                if (!exch_domain.contains("https") && exch_domain.startsWith("http")) {
                    exch_domain = exch_domain.replaceFirst("http", "https");
                    return exch_domain;
                }
                return exch_domain;
            } else {
                if ("http://www.binance.cloud/".equals(exch_domain)) {
                    exch_domain = sysConfigHelper.getCodeByDisplayName("china_exch_domain");
                    if (!exch_domain.endsWith("/")) {
                        exch_domain = exch_domain + "/";
                    }
                } else {
                    exch_domain = sysConfigHelper.getCodeByDisplayName("exch_domain");
                    if (!exch_domain.endsWith("/")) {
                        exch_domain = exch_domain + "/";
                    }
                }
                return exch_domain;
            }
        } catch (Exception e) {
            log.warn("get domain error");
        }
        return null;
    }
}
